package code1601_1700.code10_20;

import java.security.Policy;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

/**
 * 给你一个点数组 points 和一个表示角度的整数 angle ，你的位置是 location ，其中 location = [posx, posy] 且 points[i] = [xi, yi] 都表示 X-Y 平面上的整数坐标。
 *
 * 最开始，你面向东方进行观测。你 不能 进行移动改变位置，但可以通过 自转 调整观测角度。换句话说，posx 和 posy 不能改变。你的视野范围的角度用 angle 表示， 这决定了你观测任意方向时可以多宽。设 d 为你逆时针自转旋转的度数，那么你的视野就是角度范围 [d - angle/2, d + angle/2] 所指示的那片区域。
 *
 * 对于每个点，如果由该点、你的位置以及从你的位置直接向东的方向形成的角度 位于你的视野中 ，那么你就可以看到它。
 *
 * 同一个坐标上可以有多个点。你所在的位置也可能存在一些点，但不管你的怎么旋转，总是可以看到这些点。同时，点不会阻碍你看到其他点。
 *
 * 返回你能看到的点的最大数目。
 *
 * 输入：points = [[2,1],[2,2],[3,3]], angle = 90, location = [1,1]
 * 输出：3
 * 解释：阴影区域代表你的视野。在你的视野中，所有的点都清晰可见，尽管 [2,2] 和 [3,3]在同一条直线上，你仍然可以看到 [3,3] 。
 *
 * 输入：points = [[2,1],[2,2],[3,4],[1,1]], angle = 90, location = [1,1]
 * 输出：4
 * 解释：在你的视野中，所有的点都清晰可见，包括你所在位置的那个点。
 *
 * 输入：points = [[1,0],[2,1]], angle = 13, location = [1,1]
 * 输出：1
 * 解释：如图所示，你只能看到两点之一。
 */
public class _1610visiblePoints {

    /**
     * 思考：对于每个点可以计算与pos位置处的角度，最终得到最大角度看是否满足angle。满足则输出所有点。
     * 不满足的话，按权重进行角度递减。
     *
     * 方法一：二分查找。直角坐标转换为极坐标，然后计算每个点的极角。然后二分遍历进行统计
     *
     * 执行用时：     * 176 ms     * , 在所有 Java 提交中击败了     * 70.69%     * 的用户
     * 内存消耗：     * 106.5MB      * , 在所有 Java 提交中击败了     * 5.17%     * 的用户
     * @param points
     * @param angle
     * @param location
     * @return
     */
    public int visiblePoints(List<List<Integer>> points, int angle, List<Integer> location) {
        int sameCnt = 0;
        List<Double> polorDegrees = new ArrayList<>();
        int locationX = location.get(0);
        int locationY = location.get(1);
        for (int i = 0; i < points.size(); i++) {
            int x = points.get(i).get(0);
            int y = points.get(i).get(1);
            if(x == locationX&&y == locationY){
                ++sameCnt;
                continue;
            }
            Double degree = Math.atan2(y-locationY,x-locationX);
            polorDegrees.add(degree);
        }
        // 计算出所有角度后排序
        Collections.sort(polorDegrees);
        int m = polorDegrees.size();
        for (int i = 0; i < m; i++) {
            polorDegrees.add(polorDegrees.get(i)+2*Math.PI);
        }
        int maxCnt = 0;
        Double toDegree = angle*Math.PI/180;
        for (int i = 0; i < m; i++) {
            int iteration = binarySearch(polorDegrees,polorDegrees.get(i)+toDegree,false);
            maxCnt = Math.max(maxCnt,iteration-i);
        }
        return maxCnt+sameCnt;
    }

    private int binarySearch(List<Double> nums, double target, boolean lower) {
        int left = 0,right = nums.size()-1;
        int ans = nums.size();
        while (left<=right){
            int mid = (left+right)/2;
            if(nums.get(mid)>target||(lower&&nums.get(mid)>=target)){
                right = mid-1;
                ans = mid;
            }else {
                left = mid + 1;
            }
        }
        return ans;
    }

}
